package BNS.coloring;

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.PriorityQueue;
import java.util.Stack;
import java.util.StringTokenizer;
import java.util.Scanner;
import java.util.Vector;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.filechooser.FileFilter;

import BNS.graphs.GraphPack;
import BNS.graphs.Node;
import BNS.plumbing.BNSGameController;
import BNS.plumbing.Library;


/** This is a utility for creating ColoringGames and saving them in files.
	It is intended to get all the details and opportunities for error done correctly
	so that there is no load on the supervisor's brain cells during a live session.
	*/
public class GameBuilder extends JFrame implements ActionListener, ItemListener {
	/**
   * 
   */
	// Appearance and components.
	//private static final long serialVersionUID = 1L;
	private final int WINDOW_WIDTH = 500;
	private final int WINDOW_HEIGHT = 230;
	private JTextField colorField, scrambleField, timeField, payField, fileField,
		preField, gameField, sufField, packField, costToMoveField;
	//private JSlider colorSlide;
	//private JLabel gpLabel;
	private JButton submit;
	private JButton getGPack;
	private String[] gpNames;
	private int gpNum=0;
	private JComboBox typeList;
	//private String prefix;
	
	// Progress and time bars.
	private JCheckBox cbProgress = new JCheckBox("Display progress bar");
	private JCheckBox cbTime = new JCheckBox("Display time bar");
	private boolean timeBar = false; 
	private boolean progBar = false;
	
	// Bonus vectors stuff.
	/** Maps a node class to a bonus vector. */
	TreeMap<Character, Vector<Float>> bonusMap = new TreeMap<Character, Vector<Float>>();
	/** Holds all the bonus vectors that are displayed in the window. */
	TreeMap<Character, JTextField> bonusFields = new TreeMap<Character, JTextField>();
	/** Container that all the bonus panels are placed in. */
	private JPanel mainVectorPanel;
	
	// Graph pack / file chooser stuff.
	/** Container that all the graph pack information is placed in. */
	final JFileChooser fc = new JFileChooser();
	private JPanel gpackSummaryPanel;
	
	public GameBuilder(String[] args) {
		gpNames= args;
		System.err.println("making "+ args.length +" games");

		String prep= System.getProperty("PREP");
		if (prep==null) prep= "./";
		Library.setPrep(new File(prep));
		/* We discover the value of the ARCH property, which will be used
		in the Library for storing session files and exp files. */
		String arch= System.getProperty("ARCH");
		if (arch==null) arch= "./output";
		Library.setArch(new File(arch));

		setTitle("BNS Game GameBuilder");
		setSize (WINDOW_WIDTH, WINDOW_HEIGHT);
		JPanel panel= new JPanel();
		panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
		
		JPanel typePanel= new JPanel();
		typePanel.setLayout(new BoxLayout(typePanel, BoxLayout.X_AXIS));
		typePanel.add(new JLabel("game type:",JLabel.RIGHT));		
		String[] typeStrings= { "","Consensus", "Coloring", "VoterCon", 
								"Matching", "KingPawn", "MarketIS" };
		typeList= new JComboBox(typeStrings);
		typeList.setSelectedItem("Coloring");
		typeList.addActionListener(this);
		typePanel.add(typeList);
		panel.add(typePanel);		
		
		JPanel packPanel= new JPanel();
		packPanel.setLayout(new BoxLayout(packPanel, BoxLayout.X_AXIS));
		packPanel.add(new JLabel("pack file: ",JLabel.RIGHT));		
		packField= new JTextField(); packPanel.add(packField);
		packField.setEditable (false);
		getGPack = new JButton ("browse for gpack"); getGPack.addActionListener(this);
		packPanel.add(getGPack);
		panel.add(packPanel);
		
		JPanel colorPanel= new JPanel();
		colorPanel.setLayout(new BoxLayout(colorPanel, BoxLayout.X_AXIS));
		colorPanel.add(new JLabel("number of colors: ",JLabel.RIGHT));
		colorField= new JTextField(3); colorPanel.add(colorField);
		colorPanel.add(new JLabel("scramble colors: ",JLabel.RIGHT));
		scrambleField= new JTextField("1",3); colorPanel.add(scrambleField);
		panel.add(colorPanel);
		
		JPanel payPanel= new JPanel();
		payPanel.setLayout(new BoxLayout(payPanel, BoxLayout.X_AXIS));
		payPanel.add(new JLabel("normal payoff ($): ",JLabel.RIGHT));
		payField= new JTextField(6); payPanel.add(payField);
		payPanel.add(new JLabel("cost to move ($): ", JLabel.RIGHT));
		costToMoveField = new JTextField(6); payPanel.add (costToMoveField);
		//leanField= new JTextField("1.00",5); payPanel.add(leanField);
		payPanel.add(new JLabel("bonus (old): ",JLabel.RIGHT));
		fileField= new JTextField(1); payPanel.add(fileField);
		panel.add(payPanel);
		
		JPanel timePanel= new JPanel();
		timePanel.setLayout(new BoxLayout(timePanel, BoxLayout.X_AXIS));
		timePanel.add(new JLabel("game time limit: ",JLabel.RIGHT));		
		timeField= new JTextField("180",5); timePanel.add(timeField);
		panel.add(timePanel);
		
		JPanel gamePanel= new JPanel();
		gamePanel.setLayout(new BoxLayout(gamePanel, BoxLayout.X_AXIS));
		gamePanel.add(new JLabel("game file: ",JLabel.RIGHT));
		preField= new JTextField(10); gamePanel.add( preField);preField.setText("CLR-");
		gameField=new JTextField(50); gamePanel.add(gameField);
		sufField= new JTextField(".game",10); gamePanel.add( sufField);
		panel.add(gamePanel);
		
		JPanel barPanel = new JPanel();
		cbProgress.addItemListener(this);
		cbTime.addItemListener(this);
		barPanel.add( cbProgress );
		barPanel.add(cbTime);
		panel.add(barPanel);
		
		mainVectorPanel = new JPanel();
		mainVectorPanel.setLayout(new BoxLayout(mainVectorPanel, BoxLayout.Y_AXIS));
		panel.add (mainVectorPanel);
		
		gpackSummaryPanel = new JPanel();
		mainVectorPanel.setLayout(new BoxLayout(mainVectorPanel, BoxLayout.Y_AXIS));
		panel.add (gpackSummaryPanel);
		
		submit= new JButton("submit"); submit.addActionListener(this); panel.add(submit);
		getContentPane().add(panel);
		validate();	// ?
//		pack();
		setLocation(700,60);
		setVisible(true);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		nextPack();
		}
	
	private void nextPack() {
		if (gpNum < gpNames.length) {
			packField.setText(gpNames[gpNum]);
			String pack= new File(gpNames[gpNum]).getName();
	    //identField= new JTextField("0",20); payPanel.add(identField);
//			System.err.println("gpack suffix found at "+ pack.indexOf(".gpack"));
			int sufidx= pack.indexOf(".gpack");
			if (sufidx<1) sufidx= pack.length();
			String fname= pack.substring(0,sufidx);
			Pattern pattern = Pattern.compile("c=\\d*");
			Matcher matcher = pattern.matcher(fname);
			if (matcher.find()) {
				int st= matcher.start();
				int nd= matcher.end();
				String cpull= fname.substring(st+2,nd);
				int chroma= Integer.parseInt(cpull);
				//System.err.format("matched (%d,%d) found %s%n",st,nd,cpull);
				System.err.println("chromatic number found "+ chroma);
				if (chroma>2) colorField.setText(""+chroma);
				}
			gameField.setText(fname);
			}
		else if (gpNum == gpNames.length) {
			packField.setText("");
			gameField.setText("");
			}
		gpNum++;
		}
	
	private void makeAndSave() {
		File pfile= new File(packField.getText());
		System.err.println("obtaining gpack at "+ pfile);
		GraphPack gp= Library.getPack(pfile);
		if (gp==null) return;
		System.out.println (gp.graph.getNodes());
		
		String type= (String)typeList.getSelectedItem();
		//float leanPay= Float.parseFloat(leanField.getText());
		int colors= Integer.parseInt(colorField.getText());
		float pay= Float.parseFloat(payField.getText());
		float costToMove = 0;
		if (costToMoveField.getText().length() != 0)
			costToMove = Float.parseFloat(costToMoveField.getText());
		// Vector<Float> bonus= parseBonusVector(fileField.getText(),colors);
		int scramble= Integer.parseInt(scrambleField.getText());
		int time= Integer.parseInt(timeField.getText());
		String prefix= preField.getText();
		String suffix= sufField.getText();
		String gfld= prefix + gameField.getText() + suffix;
		for (Character key : bonusFields.keySet()) {
			bonusMap.put (key, parseBonusVector (bonusFields.get(key).getText(), colors));
		}
		
		System.err.println(type +", params: "+ pay +", "+ time);
		BNSGameController game= null;
		if (type.equals("Coloring"))  game= new ColoringGame (gp, colors, time, pay, costToMove, 
				bonusMap, scramble, progBar, timeBar);
		if (type.equals("Consensus")) game= new ConsensusGame (gp, colors, time, pay, costToMove,
				bonusMap, scramble, progBar, timeBar);
		if (type.equals("VoterCon" )) {
			String fileName= fileField.getText();
			float[][] payoffs= readRaggedArray(new File(fileName));
			game= new VoterGame   (gp,colors,time, pay,payoffs);
			}
//		if (type.equals("Matching"))  game= new MatchingGame(gp,colors,time,phatPay);
//		if (type.equals("KingPawn"))  game= new KingPawnGame(gp,time,leanPay,phatPay);
//		if (type.equals("MarketIS"))  game= new MarketISGame(gp,time,leanPay,phatPay);
		
		// Check that the number of colors specified for the game is at least the number of status
		// quo colors there are. Also check that for all colors c, 0<=c<=maxSQColor.
		boolean colorsOkay = true;
		PriorityQueue <Integer> sqColors = new PriorityQueue<Integer>();
		for (Node node : gp.graph.getNodes())
			if (!sqColors.contains(node.sqColor))
				sqColors.add(node.sqColor);
		
		int numColors = sqColors.size()-1;
		int minSQColor = sqColors.peek();
		Stack<Integer> sqReverse = new Stack<Integer>();
		while (!sqColors.isEmpty())
			sqReverse.push(sqColors.remove());
		int maxSQColor = sqReverse.peek();
		
		if (colors < numColors)
			colorsOkay = false;
		if (minSQColor < 0 || maxSQColor > colors)
			colorsOkay = false;
		
		if (!colorsOkay)
		{
			game = null;
			JOptionPane.showMessageDialog(GameBuilder.this,
				"Number of total colors is less than number of status quo colors,\nor status quo" +
				" colors not in the right range.\nFailed to construct game.",
				"Error with Status Quo Colors", JOptionPane.ERROR_MESSAGE);
		}
		
		if (game!=null) {
			System.err.println("game obtained: "+ game.getInfo());
			Library.putGame(new File(gfld),game);
			System.err.println("saved game at "+ gfld);
			}
		else System.err.println("FAILED to construct game ");
		}
	
	private Vector<Float> parseBonusVector(String s,int colors) {
	  String[] tokens=s.split(" ");
	  Vector<Float> bonus=new Vector<Float>();
	  /*for(int i=0;i<colors;i++) {
	    if(tokens.length>i) bonus.add(Float.parseFloat(tokens[i]);
	    else bonus.add(0);
	  }*/
	  // System.err.println(tokens.length);
	  // System.err.println(tokens[0].equals(""));
	  if(!tokens[0].equals(""))
  	  for(String current:tokens) {
  	    bonus.add(Float.parseFloat(current));
  	  }
	  return bonus;
	}
	
	/** mandated by ActionListener interface. Handles the game type menu and the submit button. */
	public void actionPerformed(ActionEvent e) { 
		Object src= e.getSource();
		
		if (src==submit) { makeAndSave(); nextPack(); }
		if (src==typeList) { 
			String type= (String)typeList.getSelectedItem();
			preField.setText("");
			if (type.equals("Coloring"))  preField.setText("CLR-");
			if (type.equals("Consensus")) preField.setText("CNS-");
			if (type.equals("VoterCon"))  preField.setText("VTR-");
//			if (type.equals("Matching"))  preField.setText("MCH-");
//			if (type.equals("KingPawn"))  {preField.setText("KaP-");colorField.setText("2");}
//			if (type.equals("MarketIS"))  {preField.setText("MIS-");colorField.setText("2");}
			}
		
	    if (src == getGPack) {
	    	bonusMap.clear();
	    	bonusFields.clear();
	    	mainVectorPanel.removeAll();
	    	gpackSummaryPanel.removeAll();
	    	
	    	// Get .gpack file with file chooser dialog.
	    	// Define our own filter that only allows .gpack files to be seen.
	    	FileFilter myFilter = new FileFilter() {
	            public boolean accept(File f) {
	                if (f != null) {
	                    if (f.isDirectory()) {
	                        return true;
	                    }
	                    String extension = getExtension(f);
	                    if (extension != null) {
	                        return ((extension.equalsIgnoreCase("gpack")) ? true : false);
	                    }
	                }
	                return false;
	            }
	            public String getDescription() {
	                return "*.gpack";
	            }
	            String getExtension(File f) {
	                String ext = null;
	                String s = f.getName();
	                int i = s.lastIndexOf('.');
	                if (i > 0 && i < s.length() - 1) {
	                    ext = s.substring(i + 1).toLowerCase();
	                }
	                return ext;
	            }
	        };
	        fc.setAcceptAllFileFilterUsed(false);
	    	fc.setFileFilter(myFilter);
	        
	    	// Show dialog, get return value.
	    	int returnVal = fc.showOpenDialog(GameBuilder.this);; 
	        if (returnVal == JFileChooser.APPROVE_OPTION) {
	            File file = fc.getSelectedFile();
	            packField.setText(file.getName());
	        } else {
	        }
	        
	        // Examine GraphPack, set up bonus map and display color information.
	        File pfile = fc.getSelectedFile(); //new File(packField.getText());
			GraphPack gp = Library.getPack(pfile);
			Vector <Node> allNodes = gp.graph.getNodes();
			for (Node node : allNodes) {
				if (!bonusMap.keySet().contains(node.nodeClass)) {
					bonusMap.put(node.nodeClass, new Vector<Float>());
				}
			}
			
			// Draw the bonus vectors in the GameBuilder.
			for (Character key : bonusMap.keySet()) {
				JPanel currentPanel = new JPanel();
				currentPanel.setLayout (new BoxLayout (currentPanel, BoxLayout.X_AXIS));
				String panelName = "bonus ";
				panelName += ((key == '?') ? "default" : key) + ": ";
				currentPanel.add (new JLabel (panelName, JLabel.RIGHT));
				JTextField currentBonus = new JTextField (15); currentPanel.add (currentBonus);
				bonusFields.put (key, currentBonus);
				currentPanel.add (currentBonus);
				mainVectorPanel.add (currentPanel);
			}
			
			// Draw the summary text area.
			JTextArea textArea = new JTextArea(allNodes.size(), 45);
	        textArea.setEditable(false);
			String gpackSummary = "";
			for (int i=0; i<allNodes.size(); i++) {
				Node current = allNodes.get(i);
				gpackSummary += current.toString() + ": " +
					"class = " + ((current.nodeClass == '?') ? "default" : current.nodeClass) +
					", status quo color = " + current.sqColor;
				if (i != allNodes.size() - 1) gpackSummary += "\n";
			}
			textArea.setText(gpackSummary);
			gpackSummaryPanel.add (textArea);
			
			// Resizes the window to accommodate all the new bonus vectors being displayed.
			mainVectorPanel.repaint();
			gpackSummaryPanel.repaint();
			validate();
			setSize (WINDOW_WIDTH, WINDOW_HEIGHT + mainVectorPanel.getHeight() +
				gpackSummaryPanel.getHeight());
			setVisible (true);
	   }

	}
	
	/** method to read a bunch of numbers to be associated with a specific game.
		This was first built to read individual payoffs for the VoterGame.
		*/
	private float[][] readRaggedArray(File fn) {
		Vector<float[]> raggarray= new Vector<float[]>();
		try {
			FileReader frdr= new FileReader(fn);
			System.err.print(" raggedArray reading: "+fn);
			BufferedReader rdr= new BufferedReader(frdr);
			String line= rdr.readLine(); 
			while(line.length()>0) {
				StringTokenizer st= new StringTokenizer(line);
				int i=0, size= st.countTokens();
				float[] nmbrs= new float[size];
				while (st.hasMoreTokens())
					nmbrs[i++]= Float.parseFloat(st.nextToken());
				raggarray.add(nmbrs);
				System.err.print(" "+nmbrs.length);
				line= rdr.readLine(); 
				}
		} catch(Exception e) { System.err.println(e); System.exit(99); }
		System.err.println(" found "+raggarray.size()+" lines");
		return (float[][])raggarray.toArray();
		}
	
	/** method to invoke to start the game building GUI and process.
		@param args an array of Strings, each holding the name of a GraphPack.
			Each GraphPack will be used to define a game with a similar name.
		*/
	public static void main(String[] args) { new GameBuilder(args); }

	@Override
	public void itemStateChanged(ItemEvent e) {
		if (cbProgress.isSelected()) progBar = true;
		if (cbTime.isSelected()) timeBar = true;
	}
	
} // EOClass GameBuilder
